home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc Source Code / Utilities / LineOps.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-22  |  13.0 KB  |  492 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        LineOps.cpp
  3.  
  4.     Contains:    Geometric operations on lines in 2-space.
  5.  
  6.     Written by:    Jens Alfke
  7.                 IntersectLines by Ken Turkowski, from Apple Technical Memorandum KT14.
  8.  
  9.     Copyright:    © 1993 - 1995 by Apple Computer, Inc., all rights reserved.
  10.  
  11. */
  12.  
  13. #ifndef _ALTPOINT_
  14. #include "AltPoint.h"            // Use C++ savvy ODPoint and ODRect
  15. #endif
  16.  
  17. #include "LineOps.h"
  18.  
  19. #ifndef _EXCEPT_
  20. #include "Except.h"
  21. #endif
  22.  
  23. #ifndef _ODDEBUG_
  24. #include "ODDebug.h"
  25. #endif
  26.  
  27. #ifndef _ODMATH_
  28. #include "ODMath.h"
  29. #endif
  30.  
  31. #include <stdlib.h>
  32.  
  33. #include <math.h>
  34.  
  35. #ifndef _ODDEBUG_
  36. #include "ODDebug.h"
  37. #endif
  38.  
  39.  
  40. enum{
  41.     A00, A01, A10, A11
  42. };
  43.  
  44.  
  45. #pragma segment ODShape
  46.  
  47.  
  48. //------------------------------------------------------------------------------
  49. // WithinEpsilon
  50. //
  51. // Returns true if the two numbers are within kEpsilon of each other.
  52. //------------------------------------------------------------------------------
  53.  
  54. Boolean
  55. WithinEpsilon( ODCoordinate a, ODCoordinate b )
  56. {
  57.     ODCoordinate diff = a-b;
  58.     return diff<=kFixedEpsilon && diff>=-kFixedEpsilon;
  59. }
  60.  
  61.  
  62.  
  63. //------------------------------------------------------------------------------
  64. // InRange
  65. //
  66. // Determine whether n falls within the range [r0,r1].
  67. // The r's need not be given in order. Unline rects, endpoints are included.
  68. //------------------------------------------------------------------------------
  69.  
  70. Boolean
  71. InRange( ODCoordinate n,  ODCoordinate r0, ODCoordinate r1 )
  72. {
  73.     if( r0<r1 )
  74.         return n>=r0 && n<=r1;
  75.     else
  76.         return n>=r1 && n<=r0;
  77. }
  78.  
  79.  
  80. //------------------------------------------------------------------------------
  81. // RangesDisjoint
  82. //
  83. // Determine whether two ranges do not overlap. They needn't be given in order.
  84. //------------------------------------------------------------------------------
  85.  
  86. Boolean
  87. RangesDisjoint( ODCoordinate a0, ODCoordinate a1,
  88.                 ODCoordinate b0, ODCoordinate b1 )
  89. {
  90.     if( a0<a1 )
  91.         return Max(b0,b1) < a0  ||  a1 < Min(b0,b1);
  92.     else
  93.         return Max(b0,b1) < a1  ||  a0 < Min(b0,b1);
  94. }
  95.  
  96.  
  97. //------------------------------------------------------------------------------
  98. // IntersectLines                        by Ken Turkowski, tweaked by Jens Alfke
  99. //
  100. // Find the intersection of the two lines determined by two pairs of points.
  101. // Returns:    kIntersection if the two segments intersect;
  102. //            kOutsideIntersection if the lines intersect beyond the ends of the segments;
  103. //            kNoIntersection if the lines are parallel, coincident or degenerate.
  104. //                        (In this last case sect will not be valid.)
  105. //------------------------------------------------------------------------------
  106.  
  107. IntersectionStatus
  108. IntersectLines( const ODPoint &p0, const ODPoint &p1,
  109.                 const ODPoint &q0, const ODPoint &q1,
  110.                 ODPoint *sect )
  111. {
  112.     double_t temp;
  113.     double_t p0x = ODFixedToFloat(p0.x);
  114.     double_t p0y = ODFixedToFloat(p0.y);
  115.     double_t p1x = ODFixedToFloat(p1.x);
  116.     double_t p1y = ODFixedToFloat(p1.y);
  117.     double_t q0x = ODFixedToFloat(q0.x);
  118.     double_t q0y = ODFixedToFloat(q0.y);
  119.     double_t q1x = ODFixedToFloat(q1.x);
  120.     double_t q1y = ODFixedToFloat(q1.y);
  121.     
  122.     // Construct the augmented matrix:
  123.     
  124.     double_t AB[2][3];
  125.     AB[0][0] = p1y - p0y;
  126.     AB[0][1] = p0x - p1x;
  127.     AB[0][2] = AB[0][0]*p0x + AB[0][1]*p0y;
  128.     
  129.     AB[1][0] = q1y - q0y;
  130.     AB[1][1] = q0x - q1x;
  131.     AB[1][2] = AB[1][0]*q0x + AB[1][1]*q0y;
  132.     
  133.     // Find the largest element (in absolute value):
  134.  
  135.     double_t pmax = fabs(AB[0][0]);
  136.     char pwhich = A00;
  137.     temp = fabs(AB[0][1]);
  138.     if( temp>pmax ) {
  139.         pmax = temp;
  140.         pwhich = A01;
  141.     }
  142.  
  143.     double_t qmax = fabs(AB[1][0]);
  144.     char qwhich = A10;
  145.     temp = fabs(AB[1][1]);
  146.     if( temp>qmax ) {
  147.         qmax = temp;
  148.         qwhich = A11;
  149.     }
  150.     
  151.     double_t max;
  152.     char which;
  153.     if( pmax > qmax ) {
  154.         max = pmax;
  155.         which = pwhich;
  156.     } else {
  157.         max = qmax;
  158.         which = qwhich;
  159.     }
  160.     
  161.     // Give up if entire matrix is zero (we were given two degenerate line segments)
  162.     if( max==0 )
  163.         return kNoIntersection;
  164.     
  165.     // Pivot around the largest element and back-substitute. If we're about to divide
  166.     // by zero, this means the two lines are parallel (or one segment is degenerate.)
  167.     
  168.     double_t sectx, secty;
  169.     
  170.     switch( which ) {
  171.         case A00:                                        // p's Δy is the greatest
  172.             temp = AB[1][0] / AB[0][0];
  173.             AB[1][1] -= temp * AB[0][1];
  174.             AB[1][2] -= temp * AB[0][2];
  175.             if( fabs(AB[1][1]) < 0.0001 )
  176.                 return kNoIntersection;
  177.             secty = AB[1][2] / AB[1][1];
  178.             sectx = (AB[0][2] - AB[0][1]*secty) / AB[0][0];
  179.             break;
  180.         case A01:                                        // p's Δx is the greatest
  181.             temp = AB[1][1] / AB[0][1];
  182.             AB[1][0] -= temp * AB[0][0];
  183.             AB[1][2] -= temp * AB[0][2];
  184.             if( fabs(AB[1][0]) < 0.0001 )
  185.                 return kNoIntersection;
  186.             sectx = AB[1][2] / AB[1][0];
  187.             secty = (AB[0][2] - AB[0][0]*sectx) / AB[0][1];
  188.             break;
  189.         case A10:                                        // q's Δy is the greatest
  190.             temp = AB[0][0] / AB[1][0];
  191.             AB[0][1] -= temp * AB[1][1];
  192.             AB[0][2] -= temp * AB[1][2];
  193.             if( fabs(AB[0][1]) < 0.0001 )
  194.                 return kNoIntersection;
  195.             secty =  AB[0][2] / AB[0][1];
  196.             sectx = (AB[1][2] - AB[1][1]*secty) / AB[1][0];
  197.             break;
  198.         case A11:                                        // q's Δx is the greatest
  199.             temp = AB[0][1] / AB[1][1];
  200.             AB[0][0] -= temp * AB[1][0];
  201.             AB[0][2] -= temp * AB[1][2];
  202.             if( fabs(AB[0][0]) < 0.0001 )
  203.                 return kNoIntersection;
  204.             sectx = AB[0][2] / AB[0][0];
  205.             secty = (AB[1][2] - AB[1][0]*sectx) / AB[1][1];
  206.             break;
  207.     }
  208.     
  209.     if( fabs(sectx)>32767.0 || fabs(secty)>32767.0 )
  210.         return kNoIntersection;
  211.     sect->x = ODFloatToFixed(sectx);
  212.     sect->y = ODFloatToFixed(secty);
  213.     
  214.     // Check whether the intersection is between endpoints of p and q.
  215.     // Use max delta of p and q to test for inclusion:
  216.     Boolean inside;
  217.     if( pwhich==A00 )
  218.         inside = InRange(sect->y, p0.y,p1.y);
  219.     else
  220.         inside = InRange(sect->x, p0.x,p1.x);
  221.     if( inside )
  222.         if( qwhich==A11 )
  223.             inside = InRange(sect->x, q0.x,q1.x);
  224.         else
  225.             inside = InRange(sect->y, q0.y,q1.y);
  226.  
  227.     if( inside )
  228.         return kIntersection;
  229.     else
  230.         return kOutsideIntersection;
  231.     
  232. }
  233.  
  234.  
  235. #if 0
  236.     // ORIGINAL FIXED-POINT VERSION:
  237.     IntersectionStatus
  238.     IntersectLines( const ODPoint &p0, const ODPoint &p1,
  239.                     const ODPoint &q0, const ODPoint &q1,
  240.                     ODPoint *sect )
  241.     {
  242.         ODCoordinate temp;
  243.         
  244.         // Construct the augmented matrix:
  245.         
  246.         ODCoordinate AB[2][3];
  247.         AB[0][0] = p1.y - p0.y;
  248.         AB[0][1] = p0.x - p1.x;
  249.         AB[0][2] = FixMul(AB[0][0],p0.x) + FixMul(AB[0][1],p0.y);
  250.         
  251.         AB[1][0] = q1.y - q0.y;
  252.         AB[1][1] = q0.x - q1.x;
  253.         AB[1][2] = FixMul(AB[1][0],q0.x) + FixMul(AB[1][1],q0.y);
  254.         
  255.         // Find the largest element (in absolute value):
  256.     
  257.         ODCoordinate pmax = Abs(AB[0][0]);
  258.         char pwhich = A00;
  259.         temp = Abs(AB[0][1]);
  260.         if( temp>pmax ) {
  261.             pmax = temp;
  262.             pwhich = A01;
  263.         }
  264.     
  265.         ODCoordinate qmax = Abs(AB[1][0]);
  266.         char qwhich = A10;
  267.         temp = Abs(AB[1][1]);
  268.         if( temp>qmax ) {
  269.             qmax = temp;
  270.             qwhich = A11;
  271.         }
  272.         
  273.         ODCoordinate max;
  274.         char which;
  275.         if( pmax > qmax ) {
  276.             max = pmax;
  277.             which = pwhich;
  278.         } else {
  279.             max = qmax;
  280.             which = qwhich;
  281.         }
  282.         
  283.         // Give up if entire matrix is zero (we were given two degenerate line segments)
  284.         if( max==0 )
  285.             return kNoIntersection;
  286.         
  287.         // Pivot around the largest element and back-substitute. If we're about to divide
  288.         // by zero, this means the two lines are parallel (or one segment is degenerate.)
  289.         
  290.         switch( which ) {
  291.             case A00:                                        // p's Δy is the greatest
  292.                 temp = ODFractDivide(AB[1][0],AB[0][0]);
  293.                 AB[1][1] -= FracMul(temp, AB[0][1]);
  294.                 AB[1][2] -= FracMul(temp, AB[0][2]);
  295.                 if( AB[1][1]==0 )
  296.                     return kNoIntersection;
  297.                 sect->y = FixDiv(AB[1][2], AB[1][1]);
  298.                 sect->x = FixDiv(AB[0][2] - FixMul(AB[0][1],sect->y), AB[0][0]);
  299.                 break;
  300.             case A01:                                        // p's Δx is the greatest
  301.                 temp = ODFractDivide(AB[1][1],AB[0][1]);
  302.                 AB[1][0] -= FracMul(temp, AB[0][0]);
  303.                 AB[1][2] -= FracMul(temp, AB[0][2]);
  304.                 if( AB[1][0]==0 )
  305.                     return kNoIntersection;
  306.                 sect->x = FixDiv(AB[1][2], AB[1][0]);
  307.                 sect->y = FixDiv(AB[0][2] - FixMul(AB[0][0],sect->x), AB[0][1]);
  308.                 break;
  309.             case A10:                                        // q's Δy is the greatest
  310.                 temp = ODFractDivide(AB[0][0],AB[1][0]);
  311.                 AB[0][1] -= FracMul(temp, AB[1][1]);
  312.                 AB[0][2] -= FracMul(temp, AB[1][2]);
  313.                 if( AB[0][1]==0 )
  314.                     return kNoIntersection;
  315.                 sect->y = FixDiv(AB[0][2], AB[0][1]);
  316.                 sect->x = FixDiv(AB[1][2] - FixMul(AB[1][1],sect->y), AB[1][0]);
  317.                 break;
  318.             case A11:                                        // q's Δx is the greatest
  319.                 temp = ODFractDivide(AB[0][1],AB[1][1]);
  320.                 AB[0][0] -= FracMul(temp, AB[1][0]);
  321.                 AB[0][2] -= FracMul(temp, AB[1][2]);
  322.                 if( AB[0][0]==0 )
  323.                     return kNoIntersection;
  324.                 sect->x = FixDiv(AB[0][2], AB[0][0]);
  325.                 sect->y = FixDiv(AB[1][2] - FixMul(AB[1][0],sect->x), AB[1][1]);
  326.                 break;
  327.         }
  328.         
  329.         // Check whether the intersection is between endpoints of p and q.
  330.         // Use max delta of p and q to test for inclusion:
  331.         Boolean inside;
  332.         if( pwhich==A00 )
  333.             inside = InRange(sect->x, p0.x,p1.x);
  334.         else
  335.             inside = InRange(sect->y, p0.y,p1.y);
  336.         if( inside )
  337.             if( qwhich==A11 )
  338.                 inside = InRange(sect->x, q0.x,q1.x);
  339.             else
  340.                 inside = InRange(sect->y, q0.y,q1.y);
  341.     
  342.         if( inside )
  343.             return kIntersection;
  344.         else
  345.             return kOutsideIntersection;
  346.         
  347.     }
  348. #endif
  349.  
  350.  
  351. //------------------------------------------------------------------------------
  352. // IntersectSegments
  353. //
  354. // Find the intersection of the two line segments determined by two pairs of points.
  355. // This is often faster than IntersectLines in that it does not bother to compute the
  356. // outside intersection point if the two segments do not intersect. In other words:
  357. // ** sect will only be valid if the function returns true!
  358. //------------------------------------------------------------------------------
  359.  
  360. Boolean
  361. IntersectSegments( const ODPoint &p0, const ODPoint &p1,
  362.                    const ODPoint &q0, const ODPoint &q1,
  363.                    ODPoint *sect )
  364. {
  365.     if( RangesDisjoint(p0.x,p1.x, q0.x,q1.x) || RangesDisjoint(p0.y,p1.y, q0.y,q1.y) )
  366.         return kODFalse;
  367.     else
  368.         return IntersectLines(p0,p1, q0,q1, sect) == kIntersection;
  369. }
  370.  
  371.  
  372. //------------------------------------------------------------------------------
  373. // GetIntersectionT
  374. //
  375. // Determines the "t" value (fractional distance) of a point on a segment.
  376. // This function assumes that sect is a point on the segment (p0,p1).
  377. // It returns 0.0 if sect=p0, 1.0 if sect=p1, and fractional values in between.
  378. //------------------------------------------------------------------------------
  379.  
  380. Fract
  381. GetIntersectionT( const ODPoint &p0, const ODPoint &p1, const ODPoint § )
  382. {
  383.     ODCoordinate dx = p1.x - p0.x;
  384.     ODCoordinate dy = p1.y - p0.y;
  385.     
  386.     if( labs(dx) > labs(dy) )
  387.         return ODFractDivide( sect.x-p0.x, dx );            // fixed / fixed = fract
  388.     else
  389.         return ODFractDivide( sect.y-p0.y, dy );
  390. }
  391.  
  392.  
  393. //------------------------------------------------------------------------------
  394. // GetLineXAtY
  395. //
  396. // Determines the x position of a line at a given y position (i.e. the x coord
  397. // of the intersection of the line and an infinite horizontal line at y.)
  398. //------------------------------------------------------------------------------
  399.  
  400. ODCoordinate
  401. GetLineXAtY( const ODPoint &p0, const ODPoint &p1, ODCoordinate y )
  402. {
  403.     if( p0.x==p1.x )
  404.         return p0.x;                            // Line is vertical
  405.     else if( y==p0.y )
  406.         return p0.x;                            // One endpoint is at y
  407.     else if( y==p1.y )
  408.         return p1.x;                            // Other endpoint is at y
  409.     else {                                        // General case:
  410.         WASSERTM(p0.y!=p1.y,"Can't get x intercept of horiz line");
  411.         ODPoint q0 (p0.x,y);
  412.         ODPoint q1 (p1.x,y);
  413.         ODPoint sect;
  414.         IntersectLines(p0,p1,q0,q1,§);
  415.         return sect.x;
  416.     }
  417. }
  418.  
  419.  
  420. //------------------------------------------------------------------------------
  421. // Distance
  422. //
  423. // Returns the distance between two points, or kODFixedInfinity on overflow.
  424. //------------------------------------------------------------------------------
  425.  
  426. ODCoordinate
  427. Distance( const ODPoint &p0, const ODPoint &p1 )
  428. {
  429.     ODCoordinate dx = p1.x-p0.x;
  430.     ODCoordinate dy = p1.y-p0.y;
  431.     
  432.     // If dx or dy overflow a signed number, then so must the distance:
  433.     if( (p1.x<p0.x) != (dx<0)  ||  (p1.y<p0.y) != (dy<0) )
  434.         return kODFixedInfinity;
  435.     
  436.     if( dx==0 )
  437.         return Abs(dy);
  438.     else if( dy==0 )
  439.         return Abs(dx);
  440.     
  441.     ODWide dx2, dy2;
  442.     ODWideMultiply(dx,dx,&dx2);                        // (16.16)^2 is 32.32
  443.     ODWideMultiply(dy,dy,&dy2);
  444.     ODWideAdd(&dx2,&dy2);
  445.     
  446.     ODCoordinate result = ODWideSquareRoot(&dx2);    // sqrt(32.32) is 16.16!
  447.     if( result >= 0 )
  448.         return result;
  449.     else
  450.         return kODFixedInfinity;                    // Overflow!
  451. }
  452.  
  453.  
  454. //------------------------------------------------------------------------------
  455. // GetLineShift
  456. //
  457. // Return an offset by which to shift points on a line to move them a certain
  458. // distance to the left (in Mac coordinates) parallel to the line.
  459. //------------------------------------------------------------------------------
  460.  
  461. void
  462. GetLineShift( ODPoint p0, ODPoint p1, ODCoordinate dist, ODPoint &delta )
  463. {
  464.     if( p0.x==p1.x ) {
  465.         if( p1.y<p0.y )
  466.             dist = -dist;
  467.         delta.x = dist;
  468.         delta.y = 0;
  469.         
  470.     } else if( p0.y==p1.y ) {
  471.         if( p1.x>p0.x )
  472.             dist = -dist;
  473.         delta.x = 0;
  474.         delta.y = dist;
  475.         
  476.     } else {
  477.         ODCoordinate len = Distance(p0,p1);
  478.         if( len==kODFixedInfinity ) {
  479.             // Distance overflows, so just scale things down
  480.             p0.x >>= 2;        p0.y >>= 2;
  481.             p1.x >>= 2;        p1.y >>= 2;
  482.             len = Distance(p0,p1);
  483.         }
  484.         ODWide w;
  485.         ODFixed rem;
  486.         ODWideMultiply(dist,p1.y-p0.y, &w);        // delta.x =  (dist*dy)/len
  487.         delta.x = ODWideDivide(&w,len, &rem);
  488.         ODWideMultiply(dist,p0.x-p1.x, &w);        // delta.y = -(dist*dx)/len
  489.         delta.y = ODWideDivide(&w,len, &rem);
  490.     }
  491. }
  492.